/** \file ListenSocket.h
 ** \date  2004-02-13
 ** \author grymse@alhem.net
**/
/*
Copyright (C) 2004-2007  Anders Hedstrom

This library is made available under the terms of the GNU GPL.

If you would like to use this library in a closed-source application,
a separate license agreement is available. For information about
the closed-source license agreement for the C++ sockets library,
please visit http://www.alhem.net/Sockets/license.html and/or
email license@alhem.net.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#ifndef _SOCKETS_ListenSocket_H
#define _SOCKETS_ListenSocket_H
#include "sockets-config.h"

#ifdef _WIN32
#include <stdlib.h>
#else
#include <errno.h>
#endif

#include "ISocketHandler.h"
#include "Socket.h"
#include "Utility.h"
#include "SctpSocket.h"
#include "Ipv4Address.h"
#include "Ipv6Address.h"
#ifdef ENABLE_EXCEPTIONS
#include "Exception.h"
#endif

#ifdef SOCKETS_NAMESPACE
namespace SOCKETS_NAMESPACE {
#endif

/** Binds incoming port number to new Socket class X.
    \ingroup basic */
template <class X>
class ListenSocket : public Socket
{
public:
    /** Constructor.
        \param h ISocketHandler reference
        \param use_creator Optional use of creator (default true) */
    ListenSocket(ISocketHandler& h,bool use_creator = true) : Socket(h), m_depth(0), m_creator(NULL)
    ,m_bHasCreate(false)
    {
        if (use_creator)
        {
            m_creator = new X(h);
            Socket *tmp = m_creator -> Create();
            if (tmp && dynamic_cast<X *>(tmp))
            {
                m_bHasCreate = true;
            }
            if (tmp)
            {
                delete tmp;
            }
        }
    }
    ~ListenSocket() {
        if (m_creator)
        {
            delete m_creator;
        }
    }

    /** Close file descriptor. */
    int Close() {
        if (GetSocket() != INVALID_SOCKET)
        {
            closesocket(GetSocket());
        }
        return 0;
    }

    /** Bind and listen to any interface.
        \param port Port (0 is random)
        \param depth Listen queue depth */
    int Bind(port_t port,int depth = 20) {
#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6
        if (IsIpv6())
        {
            Ipv6Address ad(port);
            return Bind(ad, depth);
        }
        else
#endif
#endif
        {
            Ipv4Address ad(port);
            return Bind(ad, depth);
        }
    }

    int Bind(SocketAddress& ad,int depth) {
#ifdef USE_SCTP
        if (dynamic_cast<SctpSocket *>(m_creator))
        {
            return Bind(ad, "sctp", depth);
        }
#endif
        return Bind(ad, "tcp", depth);
    }

    /** Bind and listen to any interface, with optional protocol.
        \param port Port (0 is random)
        \param protocol Network protocol
        \param depth Listen queue depth */
    int Bind(port_t port,const std::string& protocol,int depth = 20) {
#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6
        if (IsIpv6())
        {
            Ipv6Address ad(port);
            return Bind(ad, protocol, depth);
        }
        else
#endif
#endif
        {
            Ipv4Address ad(port);
            return Bind(ad, protocol, depth);
        }
    }

    /** Bind and listen to specific interface.
        \param intf Interface hostname
        \param port Port (0 is random)
        \param depth Listen queue depth */
    int Bind(const std::string& intf,port_t port,int depth = 20) {
#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6
        if (IsIpv6())
        {
            Ipv6Address ad(intf, port);
            if (ad.IsValid())
            {
                return Bind(ad, depth);
            }
            Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL);
            return -1;
        }
        else
#endif
#endif
        {
            Ipv4Address ad(intf, port);
            if (ad.IsValid())
            {
                return Bind(ad, depth);
            }
            Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL);
            return -1;
        }
    }

    /** Bind and listen to specific interface.
        \param intf Interface hostname
        \param port Port (0 is random)
        \param protocol Network protocol
        \param depth Listen queue depth */
    int Bind(const std::string& intf,port_t port,const std::string& protocol,int depth = 20) {
#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6
        if (IsIpv6())
        {
            Ipv6Address ad(intf, port);
            if (ad.IsValid())
            {
                return Bind(ad, protocol, depth);
            }
            Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL);
            return -1;
        }
        else
#endif
#endif
        {
            Ipv4Address ad(intf, port);
            if (ad.IsValid())
            {
                return Bind(ad, protocol, depth);
            }
            Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL);
            return -1;
        }
    }

    /** Bind and listen to ipv4 interface.
        \param a Ipv4 interface address
        \param port Port (0 is random)
        \param depth Listen queue depth */
    int Bind(ipaddr_t a,port_t port,int depth = 20) {
        Ipv4Address ad(a, port);
#ifdef USE_SCTP
        if (dynamic_cast<SctpSocket *>(m_creator))
        {
            return Bind(ad, "sctp", depth);
        }
#endif
        return Bind(ad, "tcp", depth);
    }
    /** Bind and listen to ipv4 interface.
        \param a Ipv4 interface address
        \param port Port (0 is random)
        \param protocol Network protocol
        \param depth Listen queue depth */
    int Bind(ipaddr_t a,port_t port,const std::string& protocol,int depth) {
        Ipv4Address ad(a, port);
        return Bind(ad, protocol, depth);
    }

#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6
    /** Bind and listen to ipv6 interface.
        \param a Ipv6 interface address
        \param port Port (0 is random)
        \param depth Listen queue depth */
    int Bind(in6_addr a,port_t port,int depth = 20) {
        Ipv6Address ad(a, port);
#ifdef USE_SCTP
        if (dynamic_cast<SctpSocket *>(m_creator))
        {
            return Bind(ad, "sctp", depth);
        }
#endif
        return Bind(ad, "tcp", depth);
    }
    /** Bind and listen to ipv6 interface.
        \param a Ipv6 interface address
        \param port Port (0 is random)
        \param protocol Network protocol
        \param depth Listen queue depth */
    int Bind(in6_addr a,port_t port,const std::string& protocol,int depth) {
        Ipv6Address ad(a, port);
        return Bind(ad, protocol, depth);
    }
#endif
#endif

    /** Bind and listen to network interface.
        \param ad Interface address
        \param protocol Network protocol
        \param depth Listen queue depth */
    int Bind(SocketAddress& ad,const std::string& protocol,int depth) {
        SOCKET s;
        if ( (s = CreateSocket(ad.GetFamily(), SOCK_STREAM, protocol)) == INVALID_SOCKET)
        {
            return -1;
        }
        if (bind(s, ad, ad) == -1)
        {
            Handler().LogError(this, "bind", Errno, StrError(Errno), LOG_LEVEL_FATAL);
            closesocket(s);
#ifdef ENABLE_EXCEPTIONS
            throw Exception("bind() failed for port " + Utility::l2string(ad.GetPort()) + ": " + StrError(Errno));
#endif
            return -1;
        }
        if (listen(s, depth) == -1)
        {
            Handler().LogError(this, "listen", Errno, StrError(Errno), LOG_LEVEL_FATAL);
            closesocket(s);
#ifdef ENABLE_EXCEPTIONS
            throw Exception("listen() failed for port " + Utility::l2string(ad.GetPort()) + ": " + StrError(Errno));
#endif
            return -1;
        }
        m_depth = depth;
        Attach(s);
        return 0;
    }

    /** Return assigned port number. */
    port_t GetPort()
    {
        return GetSockPort();
    }

    /** Return listen queue depth. */
    int GetDepth()
    {
        return m_depth;
    }

    /** OnRead on a ListenSocket receives an incoming connection. */
    void OnRead()
    {
        struct sockaddr sa;
        socklen_t sa_len = sizeof(struct sockaddr);
        SOCKET a_s = accept(GetSocket(), &sa, &sa_len);

        if (a_s == INVALID_SOCKET)
        {
            Handler().LogError(this, "accept", Errno, StrError(Errno), LOG_LEVEL_ERROR);
            return;
        }
        if (!Handler().OkToAccept(this))
        {
            Handler().LogError(this, "accept", -1, "Not OK to accept", LOG_LEVEL_WARNING);
            closesocket(a_s);
            return;
        }
        if (Handler().GetCount() >= FD_SETSIZE)
        {
            Handler().LogError(this, "accept", (int)Handler().GetCount(), "ISocketHandler fd_set limit reached", LOG_LEVEL_FATAL);
            closesocket(a_s);
            return;
        }
        Socket *tmp = m_bHasCreate ? m_creator -> Create() : new X(Handler());
#ifdef ENABLE_IPV6
        tmp -> SetIpv6( IsIpv6() );
#endif
        tmp -> SetParent(this);
        tmp -> Attach(a_s);
        tmp -> SetNonblocking(true);
        {
#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6
            if (sa_len == sizeof(struct sockaddr_in6))
            {
                struct sockaddr_in6 *p = (struct sockaddr_in6 *)&sa;
                if (p -> sin6_family == AF_INET6)
                {
                    Ipv6Address ad(p -> sin6_addr,ntohs(p -> sin6_port));
                    ad.SetFlowinfo(p -> sin6_flowinfo);
#ifndef _WIN32
                    ad.SetScopeId(p -> sin6_scope_id);
#endif
                    tmp -> SetRemoteAddress(ad);
                }
            }
#endif
#endif
            if (sa_len == sizeof(struct sockaddr_in))
            {
                struct sockaddr_in *p = (struct sockaddr_in *)&sa;
                if (p -> sin_family == AF_INET)
                {
                    Ipv4Address ad(p -> sin_addr,ntohs(p -> sin_port));
                    tmp -> SetRemoteAddress(ad);
                }
            }
        }
        tmp -> SetConnected(true);
        tmp -> Init();
        tmp -> SetDeleteByHandler(true);
        Handler().Add(tmp);
#ifdef HAVE_OPENSSL
        if (tmp -> IsSSL()) // SSL Enabled socket
        {
            // %! OnSSLAccept calls SSLNegotiate that can finish in this one call.
            // %! If that happens and negotiation fails, the 'tmp' instance is
            // %! still added to the list of active sockets in the sockethandler.
            // %! See bugfix for this in SocketHandler::Select - don't Set rwx
            // %! flags if CloseAndDelete() flag is true.
            // %! An even better fugbix (see TcpSocket::OnSSLAccept) now avoids
            // %! the Add problem altogether, so ignore the above.
            // %! (OnSSLAccept does no longer call SSLNegotiate().)
            tmp -> OnSSLAccept();
        }
        else
#endif
        {
            tmp -> OnAccept();
        }
    }

    /** Please don't use this method.
        "accept()" is handled automatically in the OnRead() method. */
        virtual SOCKET Accept(SOCKET socket, struct sockaddr *saptr, socklen_t *lenptr)
        {
                return accept(socket, saptr, lenptr);
        }

        bool HasCreator() { return m_bHasCreate; }

    void OnOptions(int,int,int,SOCKET) {
        SetSoReuseaddr(true);
    }

protected:
    ListenSocket(const ListenSocket& s) : Socket(s) {}
private:
    ListenSocket& operator=(const ListenSocket& ) { return *this; }
    int m_depth;
    X *m_creator;
    bool m_bHasCreate;
};

#ifdef SOCKETS_NAMESPACE
}
#endif

#endif // _SOCKETS_ListenSocket_H


